package com.ikas.autumn.iec104.common.util.object;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Object ObjectUtil
 * @author 黄旭辉
 */
public class ObjectUtil {

    private static Logger logger = LoggerFactory.getLogger(ObjectUtil.class);

    private static final String JAVAP = "java.";
    private static final String JAVADATESTR = "java.util.Date";

    private static final String CLASS_EXT_FILE_NAME = ".class";

    private ObjectUtil() {

    }

    /**
     * 判断是否为空
     * @param source
     * @return
     */
    public static boolean isNull(Object source) {
        return source == null;
    }

    /**
     * compare two object
     * @param actual
     * @param expected
     * @return <ul>
     * <li>if both are null, return true</li>
     * <li>return actual.{@link Object#equals(Object)}</li>
     * </ul>
     */
    public static boolean isEquals(Object actual, Object expected) {
        return actual == expected || (actual == null ? expected == null : actual.equals(expected));
    }

    /**
     * compare two object
     * <ul>
     * <strong>About result</strong>
     * <li>if v1 > v2, return 1</li>
     * <li>if v1 = v2, return 0</li>
     * <li>if v1 < v2, return -1</li>
     * </ul>
     * <ul>
     * <strong>About rule</strong>
     * <li>if v1 is null, v2 is null, then return 0</li>
     * <li>if v1 is null, v2 is not null, then return -1</li>
     * <li>if v1 is not null, v2 is null, then return 1</li>
     * <li>return v1.{@link Comparable#compareTo(Object)}</li>
     * </ul>
     * @param v1
     * @param v2
     * @return
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static <V> int compare(V v1, V v2) {
        if (v1 == null) {
            return v2 == null ? 0 : -1;
        } else {
            return v2 == null ? 1 : ((Comparable) v1).compareTo(v2);
        }
    }

    /**
     * 从jar包中获取第一个子类
     * @param <T>
     * @param jarPath    jar包的路径,不含文件分隔符
     * @param jarName    jar包名称
     * @param superClazz 父类
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T extends Object> T getFirstSubClass(String jarPath, String jarName, Class<T> superClazz) {
        JarFile jarFile;
        T t = null;
        try {
            jarFile = new JarFile(jarPath + File.separator + jarName);
        } catch (IOException e) {
            logger.error(e.getMessage());
            return null;
        }
        Enumeration<JarEntry> jarEntries = jarFile.entries();
        while (jarEntries.hasMoreElements()) {
            JarEntry jarEntry = jarEntries.nextElement();
            String name = jarEntry.getName();
            if (name.endsWith(CLASS_EXT_FILE_NAME) && !name.contains("$")) {
                String className = name.replace("/", ".").substring(0, name.lastIndexOf('.'));
                try {
                    Class<?> c = Class.forName(className);
                    if (c.getSuperclass() == superClazz) {
                        t = (T) c.newInstance();
                        break;
                    }
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }
        try {
            jarFile.close();
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }

        return t;
    }

    /**
     * 从jar包中查找并加载第一个实现类,实现类需要带上默认的构造器
     * @param <T>
     * @param jarPath        jar包的路径,不含文件分隔符
     * @param jarName        jar包名称
     * @param interfaceClazz 接口
     * @return
     */
    public static <T extends Object> T getFirstImpl(String jarPath, String jarName, Class<T> interfaceClazz) {
        JarFile jarFile;
        T t = null;
        try {
            jarFile = new JarFile(jarPath + File.separator + jarName);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            return null;
        }
        Enumeration<JarEntry> jarEntries = jarFile.entries();
        while (jarEntries.hasMoreElements()) {
            JarEntry jarEntry = jarEntries.nextElement();
            String name = jarEntry.getName();
            if (name.endsWith(CLASS_EXT_FILE_NAME) && !name.contains("$")) {
                String className = name.replace("/", ".").substring(0, name.lastIndexOf('.'));
                try {
                    Class<?> c = Class.forName(className);
                    for (Class<?> clazz : c.getInterfaces()) {
                        if (clazz == interfaceClazz) {
                            t = (T) c.newInstance();
                            break;
                        }
                    }
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }
        try {
            jarFile.close();
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
        return t;
    }

    /**
     * 从jar包中查找加载响应的类
     * @param <T>
     * @param jarPath    jar包的路径
     * @param jarName    jar包名称
     * @param superClazz 父类
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T extends Object> T loadObject(String jarPath, String jarName, Class<T> superClazz) {
        try (JarFile jarFile = new JarFile(jarPath + File.separator + jarName)) {
            Enumeration<JarEntry> jarEntries = jarFile.entries();
            while (jarEntries.hasMoreElements()) {
                JarEntry jarEntry = jarEntries.nextElement();
                String name = jarEntry.getName();
                if (name.endsWith(CLASS_EXT_FILE_NAME) && !name.contains("$")) {
                    String className = name.replace("/", ".").substring(0, name.lastIndexOf('.'));
                    Object obj = createObject(className, superClazz);
                    return obj != null ? (T) obj : null;
                }
            }
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 从jar包中查找加载所有相应的类
     * @param <T>
     * @param jarPath    jar包的路径
     * @param jarName    jar包名称
     * @param superClazz 父类
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T extends Object> List<T> loadAll(String jarPath, String jarName, Class<T> superClazz) {
        List<T> list = new ArrayList<>();
        try (JarFile jarFile = new JarFile(jarPath + File.separator + jarName)) {
            Enumeration<JarEntry> jarEntries = jarFile.entries();
            while (jarEntries.hasMoreElements()) {
                JarEntry jarEntry = jarEntries.nextElement();
                String name = jarEntry.getName();
                if (name.endsWith(CLASS_EXT_FILE_NAME)) {
                    String className = name.replace("/", ".").substring(0, name.lastIndexOf('.'));
                    Object obj = createObject(className, superClazz);
                    if (obj != null) {
                        list.add((T) obj);
                    }
                }
            }
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
        return list;
    }

    private static Object createObject(String className, Class<?> superClazz) {
        try {
            Class<?> c = Class.forName(className);
            if (c.getSuperclass() == superClazz) {
                return c.newInstance();
            } else {
                logger.error("This class {} is not inherited from the parent class {}", className, superClazz.getName());
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * @param <T>   泛型对象
     * @param json  json串
     * @param clazz 类名
     * @return
     * @Title: parseObject
     * @Description: 根据指定类名转换成对象实例
     * @author HXH
     * @date 2021-03-25 01:32:30
     */
    public static <T> T parseObject(String json, Class<?> clazz) {
        try {
            JSONObject recordObj = JSON.parseObject(json, Feature.OrderedField);
            return (T) recordObj.toJavaObject(clazz);
        } catch (Exception e) {
            logger.error(String.format("Parse Object %s throw exception: %s", clazz, e.getMessage()), e);
        }
        return null;
    }

    /**
     * 校验参数是否为空
     * @param object
     * @return
     */
    public static boolean checkObjFeildsNull(Object object){
        boolean flag = false;
        try{
            if(null == object){
                return true;
            }
            for (Field field : object.getClass().getDeclaredFields()){
                field.setAccessible(true);
                if(field.get(object) == null || "".equals(field.get(object))){
                    flag = true;
                    return flag;
                }
                if(field.get(object) != null && "".equals(field.get(object).toString().trim())){
                    flag = true;
                    return flag;
                }
            }
            return flag;
        }catch (Exception e){
            throw new RuntimeException("校验参数异常,"+e.getMessage());
        }
    }


    /**
     * 获取利用反射获取类里面的值和名称
     *
     * @param obj
     * @return
     * @throws IllegalAccessException
     */
    public static Map<String, Object> objectToMap(Object obj) throws IllegalAccessException {
        Map<String, Object> map = new HashMap<>(8);
        Class<?> clazz = obj.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            String fieldName = field.getName();
            Object value = field.get(obj);
            map.put(fieldName, value);
        }
        return map;
    }

    /**
     * 利用递归调用将Object中的值全部进行获取
     *
     * @param timeFormatStr 格式化时间字符串默认<strong>2017-03-10 10:21</strong>
     * @param obj           对象
     * @param excludeFields 排除的属性
     * @return
     * @throws IllegalAccessException
     */
    public static Map<String, String> objectToMapString(String timeFormatStr, Object obj, String... excludeFields) throws IllegalAccessException {
        Map<String, String> map = new HashMap<>(8);

        if (excludeFields.length!=0){
            List<String> list = Arrays.asList(excludeFields);
            objectTransfer(timeFormatStr, obj, map, list);
        }else{
            objectTransfer(timeFormatStr, obj, map,null);
        }
        return map;
    }

    /**
     * 递归调用函数
     *
     * @param obj           对象
     * @param map           map
     * @param excludeFields 对应参数
     * @return
     * @throws IllegalAccessException
     */
    private static Map<String, String> objectTransfer(String timeFormatStr, Object obj, Map<String, String> map, List<String> excludeFields) throws IllegalAccessException {
        boolean isExclude=false;
        //默认字符串
        String formatStr = "yyyy-MM-dd HH:mm:ss";
        //设置格式化字符串
        if (timeFormatStr != null && !timeFormatStr.isEmpty()) {
            formatStr = timeFormatStr;
        }
        if (excludeFields!=null){
            isExclude=true;
        }
        Class<?> clazz = obj.getClass();
        //获取值
        for (Field field : clazz.getDeclaredFields()) {
            String fieldName = field.getName();
            //判断是不是需要跳过某个属性
            if (isExclude&&excludeFields.contains(fieldName)){
                continue;
            }
            //设置属性可以被访问
            field.setAccessible(true);
            Object value = field.get(obj);
            value = Optional.ofNullable(value).orElse("");
            Class<?> valueClass = value.getClass();
            if (valueClass.isPrimitive()) {
                map.put(fieldName, String.valueOf(value));
            } else if (valueClass.getName().contains(JAVAP)) {
                if (valueClass.getName().equals(JAVADATESTR)) {
                    //格式化Date类型
                    SimpleDateFormat sdf = new SimpleDateFormat(formatStr);
                    Date date = (Date) value;
                    String dataStr = sdf.format(date);
                    map.put(fieldName, dataStr);
                } else {
                    map.put(fieldName, String.valueOf(value));
                }
            } else {
                objectTransfer(timeFormatStr, value, map,excludeFields);
            }
        }
        return map;
    }

}
